package io.gitee.zhangbinhub.acp.boot.exceptions

import com.fasterxml.jackson.databind.ObjectMapper
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.boot.tools.PackageTools
import jakarta.servlet.http.HttpServletResponse
import org.springframework.http.*
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.context.request.WebRequest
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler

/**
 * Create by zhangbin on 2017-8-10 16:26
 */
@ControllerAdvice
class ServletExceptionHandler(
    private val logAdapter: LogAdapter,
    private val acpWebExceptionHandler: List<AcpWebExceptionHandler>,
    private val objectMapper: ObjectMapper
) : ResponseEntityExceptionHandler() {
    @ExceptionHandler(Exception::class)
    fun handleGlobalException(ex: Exception): ResponseEntity<out Any> = handleExceptionInternal(ex)
    fun responseGlobalException(response: HttpServletResponse, ex: Exception) {
        handleExceptionInternal(ex).let { entity ->
            for ((headerKey, headerValue) in entity.headers.entries) {
                for (value in headerValue) {
                    response.addHeader(headerKey, value)
                }
            }
            response.status = entity.statusCode.value()
            objectMapper.writeValue(response.outputStream, entity.body)
        }
    }

    /**
     * 处理全局异常
     *
     * @param ex      异常类
     * @param body    协议体
     * @param headers 请求头
     * @param status  请求状态
     * @param request 请求对象
     * @return 响应对象
     */
    override fun handleExceptionInternal(
        ex: Exception,
        body: Any?,
        headers: HttpHeaders,
        status: HttpStatusCode,
        request: WebRequest
    ): ResponseEntity<Any> = handleExceptionInternal(ex)

    /**
     * 处理全局异常
     *
     * @param ex      异常类
     * @return 响应对象
     */
    private fun handleExceptionInternal(
        ex: Exception
    ): ResponseEntity<Any> {
        logAdapter.error(ex.message, ex)
        val errorResult = acpWebExceptionHandler.firstOrNull { handler -> handler.identify(ex) }?.handle(ex)
            ?: PackageTools.buildErrorResult(HttpStatus.INTERNAL_SERVER_ERROR, ex.message)
        return ResponseEntity.status(errorResult.httpStatus)
            .header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
            .body(errorResult.errorVo as Any).also { entity ->
                logAdapter.error(
                    "response error info [${errorResult.httpStatus}] [Content-Type:${entity.headers.contentType}]: ${
                        objectMapper.writeValueAsString(
                            errorResult.errorVo
                        )
                    }"
                )
            }
    }

}
